package fr.univnantes.lina.uima;
import java.util.Iterator;
import java.util.*;
import org.apache.uima.*;
import org.apache.uima.analysis_component.JCasAnnotator_ImplBase;
import org.apache.uima.analysis_engine.AnalysisEngineProcessException;
import org.apache.uima.cas.Type;
import org.apache.uima.cas.text.AnnotationIndex;
import org.apache.uima.jcas.JCas;
import java.lang.reflect.*;

import java.net.MalformedURLException;
import java.net.URL;
import org.apache.commons.jxpath.*;
import org.apache.commons.jxpath.xml.DocumentContainer;


public class TypeMapperAE extends JCasAnnotator_ImplBase {

	private static String XML_FILE = "xmlFile";
	private String sourceTypeStr;
	private String targetTypeStr;
	private Iterator<String> targetItr;
	private String constraint;
	private LinkedList<Rule> rules = new LinkedList<Rule>();
	private static URL urlXmlFile;

	//On suppose que le fichier Xml est contenu dans le répertoire resources situé dans le path du projet
	public void initialize(UimaContext context) 
	{

		try {
			//Récupération du paramètre, le nom du fichier XML
			XML_FILE = (String) context.getConfigParameterValue(XML_FILE);
			String strUrl = "file://"+System.getProperty("user.dir")+"/resources/"+XML_FILE;
			//Création d'une URL à partir de ce nom
			urlXmlFile = new URL(strUrl);
		} catch (MalformedURLException e) {
			e.printStackTrace();
		}

		//Création d'un DocumentContainer contenant notre fichier Xml
		DocumentContainer monDoc = new DocumentContainer(urlXmlFile);
		//Création d'un JXPathContext à partir de ce DocumentContainer
		JXPathContext jxpathContext = JXPathContext.newContext(monDoc);
		Iterator itRules = jxpathContext.iterate("//RULE");
		System.out.println("Nombre de règles "+jxpathContext.selectNodes("//RULE").size());

		int i = 0;
		while(itRules.hasNext())
		{
			i++;
			itRules.next();
			System.out.println("<<<REGLE n°"+i+">>>");
			//SOURCE
			String xpath = "//RULE["+i+"]/SOURCE";
			sourceTypeStr = (String)jxpathContext.getValue(xpath);

			//TARGET
			xpath = "//RULE["+i+"]/TARGET";
			targetItr = jxpathContext.iterate(xpath);
			ArrayList<String> aL = new ArrayList<String>();
			while(targetItr.hasNext()){
				String tgt = targetItr.next();
				System.out.println("Target de la règle "+i+": "+tgt);
				aL.add(tgt);
			}

			//CONSTRAINT
			xpath= "//RULE["+i+"]/CONSTRAINT";
			try{
				constraint = (String)jxpathContext.getValue(xpath);
			}catch(JXPathNotFoundException e){
				constraint = null;
			}
			System.out.println("Taille de l'Array avant l'ajout: "+aL.size());
			//AJOUT A LA LISTE
			rules.add(new Rule(sourceTypeStr,aL,constraint));
		}
	}


	public void process(JCas aJCas) throws AnalysisEngineProcessException {

		Iterator<Rule> itRules = rules.iterator();
		Rule r;
		while(itRules.hasNext())
		{
			r = itRules.next();

			sourceTypeStr = r.getSourceType();
			//Récupération du type source
			Type typeSrc = aJCas.getTypeSystem().getType(sourceTypeStr);

			//Dans le cas où le type source n'existe pas		
			if (typeSrc == null)
			{
				String errmsg = "Le type " + sourceTypeStr + " n'est pas déclaré dans le TypeSystem !";
				throw new AnalysisEngineProcessException(errmsg, new Object[]{typeSrc});			
			}

			//EXECUTION NORMALE
			try{
				//Génération des classes correspondants aux Strings sourceTypeStr et targetTypeStr
				Class SrcClass = Class.forName(sourceTypeStr);
				//Génération des constructeurs des classes source
				Constructor srcConstr = SrcClass.getConstructor(new Class [] {JCas.class});
				//Récupération de l'ensemble/index des annotations du type source
				AnnotationIndex idxSrc = (AnnotationIndex)aJCas.getAnnotationIndex(typeSrc);
				//Création d'un itérateur sur cet index
				Iterator itSrc = idxSrc.iterator();

				//Tant qu'il y a des annotations de type source
				while(itSrc.hasNext())
				{
					//Création de l'annotation courante
					Object s = srcConstr.newInstance(new Object [] {aJCas});
					s = itSrc.next();
					SrcClass.cast(s);	//On caste l'objet en type de SrcClass

					//On place l'environnement sur l'annotation source courante
					JXPathContext jxpathContext = JXPathContext.newContext(s);

					//Récupération de la contrainte
					String constraint = (String)r.getConstraint();

					// (!jxpathContext.selectNodes(constraint).isEmpty()) 
					if(((constraint!=null)&&(jxpathContext.selectNodes(constraint).size()!=0)) || constraint==null){
						Object[] args = null;

						//Récupération du début de l'annotation courante
						int begin = (Integer)jxpathContext.getValue("begin");

						//Récupération du début de l'annotation courante
						int end = (Integer)jxpathContext.getValue("end");

						//Récupération du Covered text de l'annotation courante
						String coveredText = (String)jxpathContext.getValue("coveredText");

						System.out.println("text: begin "+begin+" end "+end+" coveredText "+coveredText);

						ArrayList<String> targetArray = r.getTargetType();
						for(int i=0; i<targetArray.size();i++){
							targetTypeStr = targetArray.get(i);

							Class TgtClass = Class.forName(targetTypeStr);

							//Génération des constructeurs des classes source/target
							Constructor tgtConstr = TgtClass.getConstructor(new Class [] {JCas.class});
							//Création d'une annotation du type target
							Object t = tgtConstr.newInstance(new Object [] {aJCas});
							TgtClass.cast(t);
							jxpathContext = JXPathContext.newContext(t);
							//Récupération de la méthode addToIndexes de la classe target
							Method addToIndexes = TgtClass.getMethod("addToIndexes", new Class[] {});

							//Ajouts à l'annotation du type target
							jxpathContext.setValue("begin", begin);
							jxpathContext.setValue("end", end);
							addToIndexes.invoke(t,args);
						}


					}

				}
			}
			catch (IllegalArgumentException e) 
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			} 
			catch (IllegalAccessException e) 
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			} 
			catch (InvocationTargetException e) 
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (SecurityException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (NoSuchMethodException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InstantiationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} 
			// Attention ce catch masque tout plein de chose !
			catch (Exception e) {
				// TODO: handle exception
				e.printStackTrace();
			}
			
		}
	}
}
